package service

import (
	"context"
	"time"

	pb "micro-rulego/api/wireless/v1"
	"micro-rulego/internal/biz/channel"
	"micro-rulego/internal/biz/device"
	"micro-rulego/internal/biz/hardware"
	"micro-rulego/internal/broker"

	"github.com/go-kratos/kratos/v2/errors"
	"github.com/go-kratos/kratos/v2/log"
	"google.golang.org/protobuf/types/known/emptypb"
)

type WirelessService struct {
	pb.UnimplementedWirelessServer

	a      hardware.Adapter
	b      broker.Broker
	dMngr  device.DeviceManager
	cMngr  channel.ChannelManager
	logger *log.Helper
}

func NewWirelessService(
	a hardware.Adapter,
	b broker.Broker,
	dMngr device.DeviceManager,
	cMngr channel.ChannelManager,
	logger log.Logger) *WirelessService {
	return &WirelessService{
		a:      a,
		b:      b,
		dMngr:  dMngr,
		cMngr:  cMngr,
		logger: log.NewHelper(logger),
	}
}

func (s *WirelessService) AdapterCheck(ctx context.Context, req *emptypb.Empty) (*pb.AdapterCheckResponse, error) {
	ia, is, n, err := s.a.Check()
	if err != nil {
		s.logger.Error("AdapterCheck execute fail", err)
		return nil, errors.InternalServer("AdapterCheck execute fail ", err.Error())
	}
	return &pb.AdapterCheckResponse{
		IsAlive:        ia,
		IsScanActivate: is,
		AdapterName:    n,
	}, nil
}

func (s *WirelessService) AdapterScan(req *pb.AdapterScanRequest, conn pb.Wireless_AdapterScanServer) error {
	var ctx context.Context
	if req.GetDuration() != 0 {
		s.logger.Debugf("AdapterScan execute with %d duration", req.GetDuration())
		ctx, _ = context.WithTimeout(context.Background(), time.Duration(req.GetDuration()))
	} else {
		ctx = context.Background()
	}
	for {
		select {
		case <-ctx.Done():
			s.logger.Info("AdapterScan finish")
			return nil
		default:
			err := conn.Send(&pb.AdapterScanResponse{
				Ctx: <-s.a.Scan(req.GetIsScanActivate()),
			})
			if err != nil {
				s.logger.Info("AdapterScan exit ", err)
				s.a.Scan(false)
				return err
			}
		}
	}
}

func (s *WirelessService) AdapterConfigure(ctx context.Context, req *pb.AdapterConfigureRequest) (*pb.Response, error) {
	err := s.a.Configure(req.GetCtx())
	if err != nil {
		s.logger.Error("AdapterConfigure execute fail", err)
		return nil, errors.InternalServer("AdapterConfigure execute fail ", err.Error())
	}
	return &pb.Response{}, nil
}

func (s *WirelessService) DeviceCheck(ctx context.Context, req *emptypb.Empty) (*pb.DeviceCheckResponse, error) {
	deviceDataList, err := s.dMngr.DeivceCheck()
	if err != nil {
		s.logger.Error("DeviceCheck execute fail", err)
		return nil, errors.InternalServer("DeviceCheck execute fail ", err.Error())
	}
	dsList := make(map[uint32][]byte)
	for id, v := range deviceDataList {
		dsList[uint32(id)] = []byte(v)
	}
	return &pb.DeviceCheckResponse{
		DeviceStatusList: dsList,
	}, nil
}

func (s *WirelessService) DeviceCreate(ctx context.Context, req *pb.DeviceCreateContext) (*pb.Response, error) {
	err := s.dMngr.DeviceCreate(req.GetDeviceData())
	if err != nil {
		s.logger.Error("DeviceCreate execute fail", err)
		return nil, errors.InternalServer("DeviceCreate execute fail ", err.Error())
	}
	return &pb.Response{}, nil
}

func (s *WirelessService) DeviceDestroy(ctx context.Context, req *pb.DeviceID) (*pb.Response, error) {
	err := s.dMngr.DeviceDestroy(device.DeviceID(req.GetID()))
	if err != nil {
		s.logger.Error("DeviceDestroy execute fail", err)
		return nil, errors.InternalServer("DeviceDestroy execute fail ", err.Error())
	}
	return &pb.Response{}, nil
}

func (s *WirelessService) DeviceConnectCheck(ctx context.Context, req *pb.DeviceID) (*pb.ConnectCheckResponse, error) {
	connStatusList, err := s.dMngr.DeviceConnectCheck(device.DeviceID(req.GetID()))
	if err != nil {
		s.logger.Error("DeviceConnectCheck execute fail", err)
		return nil, errors.InternalServer("DeviceConnectCheck execute fail ", err.Error())
	}
	csList := make(map[uint32]*pb.ConnectStatus)
	for idx, idv := range connStatusList {
		csList[uint32(idx)] = &pb.ConnectStatus{
			Status:      idv.IsAvaliable,
			ConnectSpec: []byte(idv.Spec),
		}
	}
	return &pb.ConnectCheckResponse{
		ConnectStatusList: csList,
	}, nil
}

func (s *WirelessService) DeviceConnectCreate(ctx context.Context, req *pb.ConnectCreateContext) (*pb.Response, error) {
	err := s.dMngr.DeviceConnectCreate(device.DeviceID(req.GetDevID()), req.GetConnectData())
	if err != nil {
		s.logger.Error("DeviceConnectCreate execute fail", err)
		return nil, errors.InternalServer("DeviceConnectCreate execute fail ", err.Error())
	}
	return &pb.Response{}, nil
}

func (s *WirelessService) DeviceConnectDestroy(ctx context.Context, req *pb.ConnectDestroyContext) (*pb.Response, error) {
	err := s.dMngr.DeviceConnectDestroy(device.DeviceID(req.GetDevID()), device.ConnectID(req.GetConnID()))
	if err != nil {
		s.logger.Error("DeviceConnectDestroy execute fail", err)
		return nil, errors.InternalServer("DeviceConnectDestroy execute fail ", err.Error())
	}
	return &pb.Response{}, nil
}

func (s *WirelessService) ChannelCreate(ctx context.Context, req *pb.ChannelCreateContext) (*pb.ChannelID, error) {
	isDown := false
	if req.GetType() == pb.ChannelType_Channel_TYPE_DOWN {
		isDown = true
	}
	chanID, err := s.cMngr.ChannelNodeCreate(channel.NewChannelBroker(req.GetTopic(), isDown, s.b))
	if err != nil {
		s.logger.Error("ChannelCreate execute fail", err)
		return nil, errors.InternalServer("ChannelCreate execute fail ", err.Error())
	}

	return &pb.ChannelID{ID: uint32(chanID)}, nil
}

func (s *WirelessService) ChannelBind(ctx context.Context, req *pb.ChannelBindContext) (*pb.Response, error) {
	err := s.cMngr.ChannelNodeAddEntry(channel.ChannelID(req.ChanId), channel.NewEntryConn(s.dMngr, device.DeviceID(req.GetDevID()), device.ConnectID(req.ConnID)))
	if err != nil {
		s.logger.Error("ChannelBind execute fail", err)
		return nil, errors.InternalServer("ChannelBind execute fail ", err.Error())
	}
	return &pb.Response{}, nil
}

func (s *WirelessService) ChannelUnbind(ctx context.Context, req *pb.ChannelBindContext) (*pb.Response, error) {
	err := s.cMngr.ChannelNodeDeleteEntry(channel.ChannelID(req.ChanId), channel.NewEntryConn(s.dMngr, device.DeviceID(req.GetDevID()), device.ConnectID(req.ConnID)))
	if err != nil {
		s.logger.Error("ChannelUnbind execute fail", err)
		return nil, errors.InternalServer("ChannelUnbind execute fail ", err.Error())
	}
	return &pb.Response{}, nil
}

func (s *WirelessService) ChannelEnable(ctx context.Context, req *pb.ChannelID) (*pb.Response, error) {
	err := s.cMngr.ChannelEnable(channel.ChannelID(req.GetID()))
	if err != nil {
		s.logger.Error("ChannelEnable execute fail", err)
		return nil, errors.InternalServer("ChannelEnable execute fail ", err.Error())
	}
	return &pb.Response{}, nil
}

func (s *WirelessService) ChannelDisable(ctx context.Context, req *pb.ChannelID) (*pb.Response, error) {
	err := s.cMngr.ChannelDisable(channel.ChannelID(req.GetID()))
	if err != nil {
		s.logger.Error("ChannelDisable execute fail", err)
		return nil, errors.InternalServer("ChannelDisable execute fail ", err.Error())
	}
	return &pb.Response{}, nil
}
